package com.bx.implatform.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bx.imclient.IMClient;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMGroupMessage;
import com.bx.imcommon.model.IMSystemMessage;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imcommon.util.CommaTextUtils;
import com.bx.implatform.contant.Constant;
import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.dto.*;
import com.bx.implatform.entity.*;
import com.bx.implatform.enums.MessageStatus;
import com.bx.implatform.enums.MessageType;
import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.mapper.*;
import com.bx.implatform.portal.config.PortalProperties;
import com.bx.implatform.portal.entity.MessageEntity;
import com.bx.implatform.portal.service.PortalApiService;
import com.bx.implatform.service.*;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.ucc.entity.UccTerminalParamEntity;
import com.bx.implatform.ucc.service.UccApiService;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.GroupMemberVO;
import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.vo.GroupVO;
import com.bx.implatform.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP)
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements GroupService {
    private final UserService userService;
    private final GroupMemberService groupMemberService;
    private final GroupRoomService groupRoomService;
    private final GroupRoomMapper groupRoomMapper;
    private final GroupMessageMapper groupMessageMapper;
    private final FriendService friendsService;
    private final IMClient imClient;
    private final RedisTemplate<String, Object> redisTemplate;
    private final PortalProperties portalProperties;
    private final PortalApiService portalApiService;
    private final RoadMapper roadMapper;
    private final OrganMapper organMapper;
    private final GroupMapper groupMapper;
    private final UccApiService uccApiService;

    @Override
    public GroupVO createGroup(GroupVO vo) {
        UserSession session = SessionContext.getSession();
        User user = userService.getById(session.getUserId());
        // 保存群组数据
        Group group = BeanUtils.copyProperties(vo, Group.class);
        group.setOwnerId(user.getId());
        this.save(group);
        // 把群主加入群
        GroupMember member = new GroupMember();
        member.setGroupId(group.getId());
        member.setUserId(user.getId());
        member.setHeadImage(user.getHeadImageThumb());
        member.setUserNickName(user.getNickName());
        member.setRemarkNickName(vo.getRemarkNickName());
        member.setRemarkGroupName(vo.getRemarkGroupName());
        groupMemberService.save(member);
        GroupVO groupVo = findById(group.getId());
        // 推送同步消息给自己的其他终端
        sendAddGroupMessage(groupVo, Lists.newArrayList(), true);
        String content = String.format("'%s'创建群聊成功了", session.getNickName());
        this.sendTipMessage(group.getId(), null, content, true);
        // 返回
        log.info("创建群聊，群聊id:{},群聊名称:{}", group.getId(), group.getName());
        return groupVo;
    }

    @CacheEvict(key = "#vo.getId()")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public GroupVO modifyGroup(GroupVO vo) {
        UserSession session = SessionContext.getSession();
        // 校验是不是群主，只有群主能改信息
        Group group = this.getAndCheckById(vo.getId());
        // 更新成员信息
        GroupMember member = groupMemberService.findByGroupAndUserId(vo.getId(), session.getUserId());
        if (Objects.isNull(member) || member.getQuit()) {
            throw new GlobalException("您不是群聊的成员");
        }
        member.setRemarkNickName(vo.getRemarkNickName());
        member.setRemarkGroupName(vo.getRemarkGroupName());
        groupMemberService.updateById(member);
        // 群主有权修改群基本信息
        if (group.getOwnerId().equals(session.getUserId())) {
            group = BeanUtils.copyProperties(vo, Group.class);
            this.updateById(group);
        }
        log.info("修改群聊，群聊id:{},群聊名称:{}", group.getId(), group.getName());
        return convert(group, member);
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(key = "#groupId")
    @Override
    public void deleteGroup(Long groupId) {
        UserSession session = SessionContext.getSession();
        Group group = this.getById(groupId);
        if (!group.getOwnerId().equals(session.getUserId())) {
            throw new GlobalException("只有群主才有权限解除群聊");
        }
        // 群聊用户id
        List<String> userIds = groupMemberService.findUserIdsByGroupId(groupId);
        // 逻辑删除群数据
        group.setDissolve(true);
        this.updateById(group);
        // 删除成员数据
        groupMemberService.removeByGroupId(groupId);
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
        redisTemplate.delete(key);
        // 推送解散群聊提示
        String content = String.format("'%s'解散了群聊", session.getNickName());
        this.sendTipMessage(groupId, userIds, content, true);
        // 推送同步消息
        this.sendDelGroupMessage(groupId, userIds);
        log.info("删除群聊，群聊id:{},群聊名称:{}", group.getId(), group.getName());
    }

    @Override
    public void quitGroup(Long groupId) {
        String userId = SessionContext.getSession().getUserId();
        Group group = this.getById(groupId);
        if (group.getOwnerId().equals(userId)) {
            throw new GlobalException("您是群主，不可退出群聊");
        }
        // 删除群聊成员
        groupMemberService.removeByGroupAndUserId(groupId, userId);
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
        redisTemplate.opsForHash().delete(key, userId);
        // 推送退出群聊提示
        this.sendTipMessage(groupId, List.of(userId), "您已退出群聊", false);
        // 推送同步消息
        this.sendDelGroupMessage(groupId, List.of(userId));
        log.info("退出群聊，群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
    }

    @Override
    public void kickGroup(Long groupId, String userId) {
        UserSession session = SessionContext.getSession();
        Group group = this.getAndCheckById(groupId);
        if (!group.getOwnerId().equals(session.getUserId())) {
            throw new GlobalException("您不是群主，没有权限踢人");
        }
        if (userId.equals(session.getUserId())) {
            throw new GlobalException("亲，不能移除自己哟");
        }
        // 删除群聊成员
        groupMemberService.removeByGroupAndUserId(groupId, userId);
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
        redisTemplate.opsForHash().delete(key, userId.toString());
        // 推送踢出群聊提示
        this.sendTipMessage(groupId, List.of(userId), "您已被移出群聊", false);
        // 推送同步消息
        this.sendDelGroupMessage(groupId, List.of(userId));
        log.info("踢出群聊，群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId);
    }

    @Override
    public void removeGroupMembers(GroupMemberRemoveDTO dto) {
        UserSession session = SessionContext.getSession();
        Group group = this.getAndCheckById(dto.getGroupId());
        if (!group.getOwnerId().equals(session.getUserId())) {
            throw new GlobalException("您没有权限");
        }
        if (dto.getUserIds().contains(group.getOwnerId())) {
            throw new GlobalException("不允许移除群主");
        }
        if (dto.getUserIds().contains(session.getUserId())) {
            throw new GlobalException("不允许移除自己");
        }
        // 删除群聊成员
        groupMemberService.removeByGroupAndUserIds(dto.getGroupId(), dto.getUserIds());
        // 清理已读缓存
        String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, dto.getGroupId());
        dto.getUserIds().forEach(id -> redisTemplate.opsForHash().delete(key, id.toString()));
        // 推送踢出群聊提示
        this.sendTipMessage(dto.getGroupId(), dto.getUserIds(), "您已被移出群聊", false);
        // 推送同步消息
        this.sendDelGroupMessage(dto.getGroupId(), dto.getUserIds());
        log.info("踢出群聊，群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), dto.getUserIds());
    }

    @Override
    public GroupVO findById(Long groupId) {
        UserSession session = SessionContext.getSession();
        Group group = super.getById(groupId);
        if (Objects.isNull(group)) {
            throw new GlobalException("群组不存在");
        }
        GroupMember member = groupMemberService.findByGroupAndUserId(groupId, session.getUserId());
        if (Objects.isNull(member)) {
            throw new GlobalException("您未加入群聊");
        }
        return convert(group, member);
    }

    @Cacheable(key = "#groupId")
    @Override
    public Group getAndCheckById(Long groupId) {
        Group group = super.getById(groupId);
        if (Objects.isNull(group)) {
            throw new GlobalException("群组不存在");
        }
        if (group.getDissolve()) {
            throw new GlobalException("群组'" + group.getName() + "'已解散");
        }
        if (group.getIsBanned()) {
            throw new GlobalException("群组'" + group.getName() + "'已被封禁,原因:" + group.getReason());
        }
        return group;
    }

    @Override
    public List<GroupVO> findGroups() {
        UserSession session = SessionContext.getSession();
        // 查询当前用户的群id列表
        List<GroupMember> groupMembers = groupMemberService.findByUserId(session.getUserId());
        // 一个月内退的群可能存在退群前的离线消息,一并返回作为前端缓存
        groupMembers.addAll(groupMemberService.findQuitInMonth(session.getUserId()));
        if (groupMembers.isEmpty()) {
            return new LinkedList<>();
        }
        // 拉取群列表
        List<Long> ids = groupMembers.stream().map((GroupMember::getGroupId)).collect(Collectors.toList());
        LambdaQueryWrapper<Group> groupWrapper = Wrappers.lambdaQuery();
        groupWrapper.in(Group::getId, ids);
        List<Group> groups = this.list(groupWrapper);
        // 转vo
        return groups.stream().map(group -> {
            GroupMember member =
                    groupMembers.stream().filter(m -> group.getId().equals(m.getGroupId())).findFirst().get();
            return convert(group, member);
        }).collect(Collectors.toList());
    }

    @Override
    public void invite(GroupInviteDTO dto) {
        UserSession session = SessionContext.getSession();
        Group group = this.getAndCheckById(dto.getGroupId());
        GroupMember member = groupMemberService.findByGroupAndUserId(dto.getGroupId(), session.getUserId());
        if (Objects.isNull(group) || member.getQuit()) {
            throw new GlobalException("您不在群聊中,邀请失败");
        }
        // 群聊人数校验
        List<GroupMember> members = groupMemberService.findByGroupId(dto.getGroupId());
        long size = members.stream().filter(m -> !m.getQuit()).count();
        if (dto.getFriendIds().size() + size > Constant.MAX_LARGE_GROUP_MEMBER) {
            throw new GlobalException("群聊人数不能大于" + Constant.MAX_LARGE_GROUP_MEMBER + "人");
        }
        // 找出好友信息
        List<Friend> friends = friendsService.findByFriendIds(dto.getFriendIds());
        if (dto.getFriendIds().size() != friends.size()) {
            throw new GlobalException("部分用户不是您的好友，邀请失败");
        }
        // 批量保存成员数据
        List<GroupMember> groupMembers = friends.stream().map(f -> {
            Optional<GroupMember> optional =
                    members.stream().filter(m -> m.getUserId().equals(f.getFriendId())).findFirst();
            GroupMember groupMember = optional.orElseGet(GroupMember::new);
            groupMember.setGroupId(dto.getGroupId());
            groupMember.setUserId(f.getFriendId());
            UserVO userVO = userService.findUserById(f.getFriendId());
            groupMember.setUserName(userVO.getUserName());
            groupMember.setUserNickName(f.getFriendNickName());
            groupMember.setHeadImage(f.getFriendHeadImage());
            groupMember.setCreatedTime(new Date());
            groupMember.setQuit(false);
            return groupMember;
        }).collect(Collectors.toList());
        if (!groupMembers.isEmpty()) {
            groupMemberService.saveOrUpdateBatch(group.getId(), groupMembers);
        }
        // 推送同步消息给被邀请人
        for (GroupMember m : groupMembers) {
            GroupVO groupVo = convert(group, m);
            sendAddGroupMessage(groupVo, List.of(m.getUserId()), false);
        }
        // 推送进入群聊消息
        List<String> userIds = groupMemberService.findUserIdsByGroupId(dto.getGroupId());
        String memberNames = groupMembers.stream().map(GroupMember::getShowNickName).collect(Collectors.joining(","));
        String content = String.format("'%s'邀请'%s'加入了群聊", session.getNickName(), memberNames);
        this.sendTipMessage(dto.getGroupId(), userIds, content, true);
        log.info("邀请进入群聊，群聊id:{},群聊名称:{},被邀请用户id:{}", group.getId(), group.getName(),
                dto.getFriendIds());
    }

    @Override
    public List<GroupMemberVO> findGroupMembers(Long groupId) {
        Group group = getAndCheckById(groupId);
        List<GroupMember> members = groupMemberService.findByGroupId(groupId);
        members.forEach(m -> {
            if (StringUtils.isEmpty(m.getUserName())) {
                UserVO userVO = userService.findUserById(m.getUserId());
                m.setUserName(userVO.getUserName());
            }
        });
        List<String> userIds = members.stream().map(GroupMember::getUserId).collect(Collectors.toList());
        List<String> onlineUserIds = imClient.getOnlineUser(userIds);
        // 返回群成员信息的时候，去融合通讯那边查询一下分机号和调度员ID一并返回给前端，后续邀请人员时用得到
        UccTerminalParamEntity fj_param = new UccTerminalParamEntity();
        return members.stream().map(m -> {
            GroupMemberVO vo = BeanUtils.copyProperties(m, GroupMemberVO.class);
            vo.setShowNickName(m.getShowNickName());
            vo.setShowGroupName(StrUtil.blankToDefault(m.getRemarkGroupName(), group.getName()));
            vo.setOnline(onlineUserIds.contains(m.getUserId()));
            vo.setRhtx_ddy(uccApiService.getUserList(null, m.getUserId(), null, null, null, null, "1", "10", null, null));
            fj_param.setEmployeeName(m.getUserId());
            vo.setRhtx_fj(uccApiService.queryAll(fj_param));
            return vo;
        }).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList());
    }

    /**
     * 消息免打扰
     *
     * @param dto
     */
    @Override
    public boolean muteGroupMembers(GroupMemberMuteDTO dto) {
        String userId = SessionContext.getSession().getUserId();
        boolean b = groupMemberService.update(Wrappers.lambdaUpdate(GroupMember.class)
                .eq(GroupMember::getGroupId, dto.getGroupId())
                .eq(GroupMember::getUserId, userId)
                .eq(GroupMember::getQuit, false)
                .set(GroupMember::getMute, dto.getMute()));
        if (b) {
            // 推送群聊免打扰消息
            List<String> userIds = groupMemberService.findUserIdsByGroupId(dto.getGroupId());
            String content = String.format(dto.getMute() == 1 ? "%s开启了群消息免打扰" : "%s关闭了群消息免打扰", SessionContext.getSession().getNickName());
            this.sendTipMessage(dto.getGroupId(), userIds, content, true);
            log.info("群消息免打扰，群聊id:{},免打扰状态:{}", dto.getGroupId(), dto.getMute() == 1);
        }
        return b;
    }

    /**
     * 自动入群
     *
     * @param dto
     */
    @Override
    public void automaticGroup(GroupAutomaticDTO dto) {
        List<String> userIdList = new ArrayList<String>();
        userIdList.add(dto.getUserId());
        // 判断是否已经在群中
        if (!groupMemberService.isInGroup(dto.getGroupId(), userIdList)) {
            // 路段的组织机构ID
            String roadOrganId = "";
            Road road = roadMapper.selectById(dto.getRoadId());
            if (Objects.isNull(road)) {
                throw new GlobalException("路段不存在，请核实路段ID是否正确");
            }
            // 查询路段的部门信息
            Organ organRoad = organMapper.selectById(road.getOrganId());
            // 判断路段的部门是否是分公司
            if(!"Corporation".equals(organRoad.getTypeId())){
                // 查找路段所属部门的上一级部门信息
                Organ organ = organMapper.selectById(organRoad.getParentId());
                if(!"Corporation".equals(organ.getTypeId())){
                    throw new GlobalException("路段所属组织存在问题，禁止入群");
                } else {
                    roadOrganId = organ.getId();
                }
            } else {
                roadOrganId = organRoad.getId();
            }
            // 查询用户
            User user = userService.getById(dto.getUserId());
            // 查询用户的部门信息
            Organ organUser = organMapper.selectById(user.getOrganId());
            // 查找用户部门的上级部门列表信息
            List<String> userOrgHierarchy = new ArrayList<>();
            Organ tempOrg = organUser;
            while (tempOrg != null) {
                userOrgHierarchy.add(tempOrg.getId());
                if (tempOrg.getParentId() != null) {
                    tempOrg = organMapper.selectById(tempOrg.getParentId());
                } else {
                    break;
                }
            }
            // 如果用户部门的上级部门列表中不存在路段的部门ID，则提示用户不在该路段，无法加入群聊
            if (!userOrgHierarchy.contains(roadOrganId)) {
                throw new GlobalException("您不在该路段，无法加入群聊");
            }
            // 校验群
            Group group = this.getAndCheckById(dto.getGroupId());
            // 群聊人数校验
            List<GroupMember> members = groupMemberService.findByGroupId(dto.getGroupId());
            long size = members.stream().filter(m -> !m.getQuit()).count();
            if ((1 + size) > Constant.MAX_LARGE_GROUP_MEMBER) {
                throw new GlobalException("群聊人数不能大于" + Constant.MAX_LARGE_GROUP_MEMBER + "人");
            }
            // 保存成员数据
            GroupMember groupMember = new GroupMember();
            groupMember.setGroupId(dto.getGroupId());
            groupMember.setUserId(dto.getUserId());
            groupMember.setUserName(user.getUserName());
            groupMember.setUserNickName(user.getNickName());
            groupMember.setHeadImage(user.getHeadImage());
            groupMember.setCreatedTime(new Date());
            groupMember.setQuit(false);
            List<GroupMember> groupMembers = new ArrayList<>();
            groupMembers.add(groupMember);
            groupMemberService.saveOrUpdateBatch(group.getId(), groupMembers);
            // 推送同步消息给被邀请人
            for (GroupMember m : groupMembers) {
                GroupVO groupVo = convert(group, m);
                sendAddGroupMessage(groupVo, List.of(m.getUserId()), false);
            }
            // 推送进入群聊消息
            List<String> userIds = groupMemberService.findUserIdsByGroupId(dto.getGroupId());
            String memberNames = groupMembers.stream().map(GroupMember::getShowNickName).collect(Collectors.joining(","));
            String content = String.format("%s通过事件加入了群聊", memberNames);
            this.sendTipMessage(dto.getGroupId(), userIds, content, true);
            log.info("自动加入群聊，群聊id:{},群聊名称:{},加入用户id:{}", group.getId(), group.getName(), dto.getUserId());
        }
    }

    /**
     * 绑定群聊和会议室关系
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public boolean groupRoomAdd(GroupRoomDTO dto) {
        GroupRoom groupRoom = new GroupRoom();
        // 查询是否有进行中的会议
        Wrapper<GroupRoom> wrapper = new LambdaQueryWrapper<GroupRoom>()
                .eq(GroupRoom::getGroupId, dto.getGroupId())
                .eq(GroupRoom::getRoomStatus, 0);
        if(groupRoomMapper.selectOne(wrapper) != null){
            throw new GlobalException("该群聊有进行中的会议，请勿重复创建");
        }
        groupRoom.setUserId(SessionContext.getSession().getUserId());
        BeanUtils.copyProperties(dto, groupRoom);
        groupRoom.setRoomStatus(0);// 生效状态
        groupRoom.setCreatedTime(new Date());
        boolean b = groupRoomService.save(groupRoom);
        if (b) {
            // 推送发起视频或语音消息
            List<String> userIds = groupMemberService.findUserIdsByGroupId(dto.getGroupId());
            String content = String.format("0".equals(dto.getRoomType()) ? "%s发起了视频会议" : "%s发起了语音会议", SessionContext.getSession().getNickName());
            this.sendTipMessage(dto.getGroupId(), userIds, content, true);
            log.info("群会议消息，发起人:{},会议类型:{}", SessionContext.getSession().getUserId() + ":" + SessionContext.getSession().getNickName(), "0".equals(dto.getRoomType()) ? "视频" : "语音");
            // todo 后续需要集成门户的持续通知接口，目前先用消息通知接口代替
            // 卡片消息
            UserSession session = SessionContext.getSession();
            MessageEntity message2 = new MessageEntity();
            String truncatedContent = org.apache.commons.lang3.StringUtils.abbreviate(session.getNickName() + "邀请您进行" + ("0".equals(dto.getRoomType()) ? "视频会议" : "语音会议"), 20);
            message2.setUserIdList(dto.getReceivers());
            message2.setMsgType("action_card");
            MessageEntity.ActionCard actionCard = new MessageEntity.ActionCard();
            actionCard.setTitle(("0".equals(dto.getRoomType()) ? "视频" : "语音") + "会议邀请");
            actionCard.setMarkdown("会议通知");
            actionCard.setSingleTitle(truncatedContent);
            actionCard.setSingleUrl(portalProperties.getChatUrl());
            message2.setActionCard(actionCard);
            try {
                portalApiService.sendMsg(message2);
            } catch (Exception e) {
                log.error("发送群聊消息失败:{}", e.getMessage());
            }
            log.info("发送群聊消息，发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getGroupId(), "语音或视频会议邀请");
            // todo 给pc端发送webSocket消息用于接收语音视频会议消息
            dto.getReceivers().forEach(userId ->{
                IMSystemMessage<Map<String, Object>> sendMessage = new IMSystemMessage<>();
                String socketUrl = portalProperties.getVideoUrl();
                sendMessage.setRecvTerminals(Collections.singletonList(IMTerminalType.LARGE_PC.code()));
                sendMessage.setSendResult(false);
                // 给谁发
                sendMessage.setRecvIds(List.of(userId));
                // 邀请人
                String inviteUserId = SessionContext.getSession().getUserId();
                // 接收人 userId
                // 会议室ID
                String roomName = dto.getRoomName();
                // 发送的内容
                socketUrl = socketUrl.replace("{{user}}", userId)
                        .replace("{{roomName}}", roomName);
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("socketTitle", SessionContext.getSession().getNickName()+"邀请您进行"+("0".equals(dto.getRoomType()) ? "视频" : "语音")+"会议");
                resultMap.put("socketUrl", socketUrl);
                resultMap.put("groupId", dto.getGroupId());
                sendMessage.setData(resultMap);
                imClient.sendSystemMessage(sendMessage);
            });

        }
        return b;
    }

    /**
     * 根据会议室id查询群聊id
     *
     * @param roomId
     * @return
     */
    @Override
    public GroupRoomDTO findGroupIdByRoomId(String roomId) {
        Wrapper<GroupRoom> wrapper = new LambdaQueryWrapper<GroupRoom>()
                .eq(GroupRoom::getRoomStatus, 0)
                .and(wrapper1 ->
                        wrapper1.eq(GroupRoom::getRoomId, roomId)
                                .or()
                                .eq(GroupRoom::getRoomNum, roomId)
                                .or()
                                .eq(GroupRoom::getRoomName, roomId)
                );
        GroupRoom groupRoom = groupRoomMapper.selectOne(wrapper);
        if (groupRoom == null) {
            return null;
        }
        // 查询群聊名称和发起人名称
        Group group = groupMapper.selectOne(new LambdaQueryWrapper<Group>().eq(Group::getId, groupRoom.getGroupId()));
        User user = userService.getById(groupRoom.getUserId());
        GroupRoomDTO roomDTO = new GroupRoomDTO();
        BeanUtils.copyProperties(groupRoom, roomDTO);
        roomDTO.setGroup(group);
        roomDTO.setUser(user);
        return roomDTO;
    }

    /**
     * 删除群聊和会议室的绑定关系
     *
     * @param roomId
     * @return
     */
    @Override
    @Transactional
    public boolean groupRoomDel(String roomId) {
        GroupRoomDTO dto = findGroupIdByRoomId(roomId);
        Wrapper<GroupRoom> wrapper = new LambdaQueryWrapper<GroupRoom>()
                .eq(GroupRoom::getRoomStatus, 0)
                .and(wrapper1 ->
                        wrapper1.eq(GroupRoom::getRoomId, roomId)
                                .or()
                                .eq(GroupRoom::getRoomNum, roomId)
                                .or()
                                .eq(GroupRoom::getRoomName, roomId)
                );
        GroupRoom groupRoom = groupRoomMapper.selectOne(wrapper);
        groupRoom.setClosedTime(new Date());
        groupRoom.setRoomStatus(1);
        boolean b = groupRoomMapper.updateById(groupRoom) > 0;
        if (b) {
            // 推送发起视频或语音消息
            List<String> userIds = groupMemberService.findUserIdsByGroupId(dto.getGroupId());
            String content = String.format("0".equals(dto.getRoomType()) ? "%s关闭了视频会议" : "%s关闭了语音会议", SessionContext.getSession().getNickName());
            this.sendTipMessage(dto.getGroupId(), userIds, content, true);
            log.info("群会议消息，发起人:{},会议类型:{}", SessionContext.getSession().getUserId() + ":" + SessionContext.getSession().getNickName(), "0".equals(dto.getRoomType()) ? "视频" : "语音");
        }
        return b;
    }

    /**
     * 根据群聊id查询会议室id
     *
     * @param groupId
     * @param roomStatus
     * @return
     */
    @Override
    public GroupRoomDTO findRoomIdByGroupId(Long groupId, Integer roomStatus) {
        Wrapper<GroupRoom> wrapper = new LambdaQueryWrapper<GroupRoom>()
                .eq(GroupRoom::getGroupId, groupId)
                .eq(GroupRoom::getRoomStatus, roomStatus != null ? roomStatus : 0);
        GroupRoom groupRoom = groupRoomMapper.selectOne(wrapper);
        if (groupRoom == null) {
            return null;
        }
        Group group = groupMapper.selectOne(new LambdaQueryWrapper<Group>().eq(Group::getId, groupRoom.getGroupId()));
        User user = userService.getById(groupRoom.getUserId());
        GroupRoomDTO roomDTO = new GroupRoomDTO();
        BeanUtils.copyProperties(groupRoom, roomDTO);
        roomDTO.setGroup(group);
        roomDTO.setUser(user);
        return roomDTO;
    }

    /**
     * 关闭群聊会议室
     *
     * @param groupId
     * @param page
     * @param size
     * @param roomType
     * @return
     */
    @Override
    public List<GroupRoomDTO> closeRoomsByGroupId(Long groupId, Long page, Long size, String roomType) {
        page = page > 0 ? page : 1;
        size = size > 0 ? size : 10;
        long stIdx = (page - 1) * size;
        // 查询已关闭群聊信息
        QueryWrapper<GroupRoom> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(GroupRoom::getGroupId, groupId)
                .eq(GroupRoom::getRoomStatus, 1)
                .eq(GroupRoom::getRoomType, roomType)
                .orderByDesc(GroupRoom::getId)
                .last("limit " + stIdx + "," + size);
        List<GroupRoom> groupRoomList = groupRoomService.list(wrapper);
        Group group = groupMapper.selectOne(new LambdaQueryWrapper<Group>().eq(Group::getId, groupId));
        List<GroupRoomDTO> roomDTOList =
                groupRoomList.stream().map(m -> {
                    User user = userService.getById(m.getUserId());
                    GroupRoomDTO roomDTO = new GroupRoomDTO();
                    BeanUtils.copyProperties(m, roomDTO);
                    roomDTO.setGroup(group);
                    roomDTO.setUser(user);
                    return roomDTO;
                }).collect(Collectors.toList());
        log.info("根据群id获取失效会议室信息,群聊id:{}，会议室类型:{}", groupId, roomType);
        return roomDTOList;
    }

    private void sendTipMessage(Long groupId, List<String> recvIds, String content, Boolean sendToAll) {
        UserSession session = SessionContext.getSession();
        // 消息入库
        GroupMessage message = new GroupMessage();
        message.setContent(content);
        message.setType(MessageType.TIP_TEXT.code());
        message.setStatus(MessageStatus.UNSEND.code());
        message.setSendTime(new Date());
        message.setSendNickName(session.getNickName());
        message.setGroupId(groupId);
        message.setSendId(session.getUserId());
        message.setRecvIds(sendToAll ? "" : CommaTextUtils.asText(recvIds));
        groupMessageMapper.insert(message);
        // 推送
        GroupMessageVO msgInfo = BeanUtils.copyProperties(message, GroupMessageVO.class);
        IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
        sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        if (CollUtil.isEmpty(recvIds)) {
            // 为空表示向全体发送
            List<String> userIds = groupMemberService.findUserIdsByGroupId(groupId);
            sendMessage.setRecvIds(userIds);
        } else {
            sendMessage.setRecvIds(recvIds);
        }
        sendMessage.setData(msgInfo);
        sendMessage.setSendResult(false);
        sendMessage.setSendToSelf(false);
        imClient.sendGroupMessage(sendMessage);
    }

    private GroupVO convert(Group group, GroupMember member) {
        GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class);
        vo.setRemarkGroupName(member.getRemarkGroupName());
        vo.setRemarkNickName(member.getRemarkNickName());
        vo.setShowNickName(member.getShowNickName());
        vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
        vo.setQuit(member.getQuit());
        Wrapper<GroupRoom> wrapper = new LambdaQueryWrapper<GroupRoom>()
                .eq(GroupRoom::getRoomStatus, 0)
                .eq(GroupRoom::getGroupId, group.getId());
        GroupRoom groupRoom = groupRoomMapper.selectOne(wrapper, false);
        if (groupRoom != null) {
            vo.setRoomId(groupRoom.getRoomId());
            vo.setRoomType(groupRoom.getRoomType());
        }
        return vo;
    }

    private void sendAddGroupMessage(GroupVO group, List<String> recvIds, Boolean sendToSelf) {
        UserSession session = SessionContext.getSession();
        GroupMessageVO msgInfo = new GroupMessageVO();
        msgInfo.setContent(JSON.toJSONString(group));
        msgInfo.setType(MessageType.GROUP_NEW.code());
        msgInfo.setSendTime(new Date());
        msgInfo.setGroupId(group.getId());
        msgInfo.setSendId(session.getUserId());
        IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
        sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        sendMessage.setRecvIds(recvIds);
        sendMessage.setData(msgInfo);
        sendMessage.setSendResult(false);
        sendMessage.setSendToSelf(sendToSelf);
        imClient.sendGroupMessage(sendMessage);
    }

    private void sendDelGroupMessage(Long groupId, List<String> recvIds) {
        UserSession session = SessionContext.getSession();
        GroupMessageVO msgInfo = new GroupMessageVO();
        msgInfo.setType(MessageType.GROUP_DEL.code());
        msgInfo.setSendTime(new Date());
        msgInfo.setGroupId(groupId);
        msgInfo.setSendId(session.getUserId());
        IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
        sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        sendMessage.setRecvIds(recvIds);
        sendMessage.setData(msgInfo);
        sendMessage.setSendResult(false);
        sendMessage.setSendToSelf(false);
        imClient.sendGroupMessage(sendMessage);
    }
}
